#include <stdlib.h>
#include <vector>
#include <string>
#include <iostream>
#include <assert.h>

#include "define.h"
#include "ioODB.h"

void write_strings(H5::Group& group, const H5std_string& name, const svector &strings)
{
	try {
		using namespace H5;

		//
		int lenString = 0;
		for (auto &str : strings) { lenString = MAX(lenString, str.size() + 1); }

		//
		char** chars = new char*[strings.size()];
		chars[0] = new char[strings.size()*(lenString)];
		for (int i = 0; i < strings.size(); i++) {
			chars[i] = chars[0] + i*(lenString);
			//for (int j = 0; j < maxSize + 1; j++) chars[i][j] = '\0';
			sprintf(chars[i], strings[i].c_str());
		}

		//
		hsize_t dims[1] = { strings.size() };
		DataSpace dataspace(1, dims);

		//	
		hid_t memtype = H5Tcopy(H5T_C_S1);
		H5Tset_size(memtype, lenString);
		H5Tset_strpad(memtype, H5T_STR_NULLTERM);
		DataType dataType(memtype);

		DataSet dataset = group.createDataSet(name, dataType, dataspace);

		dataset.write(chars[0], dataType);

		//
		//dataset.close();
		delete[] chars[0];
		delete[] chars;
	}
	catch (...)
	{
		std::cout
			<< "\nERROR : failed to write data '" << name
			<< "' in group '" << group.getObjName() << "'" << std::endl;
		return;
	}
}

void read_strings(H5::Group& group, const H5std_string& name, svector& strings)
{
	try {
		using namespace H5;

		//
		DataSet dataset = group.openDataSet(name);
		DataSpace filespace = dataset.getSpace();
		DataType datatype = dataset.getDataType();

		if (datatype == DataType(PredType::NATIVE_CHAR)) {
			//
			int rank = filespace.getSimpleExtentNdims();
			assert(rank == 2);

			//
			std::vector<hsize_t> dims(rank);
			rank = filespace.getSimpleExtentDims(&dims[0]);

			//
			DataSpace myspace(filespace);

			//
			char **chars = new char*[dims[0]];
			chars[0] = new char[dims[0] * dims[1]];
			for (int i = 0; i < dims[0]; i++) chars[i] = chars[0] + i* dims[1];

			// Read data from file to buffer.
			dataset.read(chars[0], PredType::NATIVE_CHAR, myspace, filespace);

			//
			strings.clear();
			for (int i = 0; i < dims[0]; i++) strings.push_back(chars[i]);

			//
			delete[] chars[0];
			delete[] chars;
		}
		else if (datatype == DataType(H5T_STRING, datatype.getSize())) {
			//
			int rank = filespace.getSimpleExtentNdims();
			assert(rank == 1);

			std::vector<hsize_t> dims(rank);
			rank = filespace.getSimpleExtentDims(&dims[0]);

			//
			int lengthStr = H5Tget_size(datatype.getId());

			//
			char **chars = new char*[dims[0]];
			chars[0] = new char[dims[0] * lengthStr];
			for (int i = 0; i < dims[0]; i++) chars[i] = chars[0] + i* lengthStr;

			// Read data from file to buffer.
			dataset.read(chars[0], datatype, filespace, filespace);

			//
			strings.clear();
			for (int i = 0; i < dims[0]; i++) {
				//string_t str(lengthStr, '\0'); 
				//strncpy(&str[0], chars[i], lengthStr);
				//strings.push_back(str);
				strings.push_back(chars[i]);
			}

			//
			delete[] chars[0];
			delete[] chars;
		}
		else {
			throw string_t("ERROR : unknow string datatype");
		}
	}
	catch (...)
	{
		std::cout
			<< "\nERROR : failed to read data '" << name
			<< "' in group '" << group.getObjName() << "'" << std::endl;
		return;
	}
}

void write_string(H5::Group& group, const H5std_string& name, const string_t &data)
{
	try {
		using namespace H5;

		DataSpace dataspace(H5S_SCALAR);

		hid_t atype = H5Tcopy(H5T_C_S1);
		H5Tset_size(atype, data.size());
		H5Tset_strpad(atype, H5T_STR_NULLTERM);

		DataSet dataset = group.createDataSet(name, DataType(atype), DataSpace(H5S_SCALAR));
		dataset.write(data, DataType(atype));
	}
	catch (...)
	{
		std::cout
			<< "\nERROR : failed to write data '" << name
			<< "' in group '" << group.getObjName() << "'" << std::endl;
		return;
	}
}

void read_string(H5::Group& group, const H5std_string& name, string_t &data)
{
	try {
		using namespace H5;

		DataSet   dataset = group.openDataSet(name);
		DataSpace filespace = dataset.getSpace();

		int datasize = filespace.getSimpleExtentNpoints();
		if (datasize != 1) {
			printf("\nERROR : %s is not a scalar, size = %d",
				name.c_str(), filespace.getSimpleExtentNpoints());
			throw;
		}

		dataset.read(data, dataset.getDataType(), filespace, filespace);
	}
	catch (...)
	{
		std::cout
			<< "\nERROR : failed to read data '" << name
			<< "' in group '" << group.getObjName() << "'" << std::endl;
		return;
	}
}